﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cqrs.Infrastructure;
using Cqrs.Infrastructure.Database;
using System.Reflection;

// This code is copied directly from Simple.Cqrs EventStore.cs:
// https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/EventStore.cs
namespace EventStore
{

    public class InMemory : IEventStore
    {
        private readonly IEventPublisher _publisher;

        public IEventPublisher Publisher
        {
            get
            {
                return _publisher;
            }
        }

        public InMemory(IEventPublisher publisher)
        {
            _publisher = publisher;
        }

        private readonly Dictionary<Guid, AggregateRootModel> _aggregateRoots = new Dictionary<Guid, AggregateRootModel>();
        private readonly Dictionary<Guid, List<EventDescriptor>> _events = new Dictionary<Guid, List<EventDescriptor>>();

        public void SaveEvents(Guid aggregateId,
            IEnumerable<Event> events, int expectedVersion, Type aggregateRootType)
        {
            List<EventDescriptor> eventDescriptors;
            if (!_events.TryGetValue(aggregateId, out eventDescriptors))
            {
                eventDescriptors = new List<EventDescriptor>();
                _events.Add(aggregateId, eventDescriptors);

                AggregateRootModel rootModel;
                if (!_aggregateRoots.TryGetValue(aggregateId, out rootModel))
                        _aggregateRoots.Add(aggregateId,
                                new AggregateRootModel(aggregateId, aggregateRootType.AssemblyQualifiedName));
            }
            else if (eventDescriptors[eventDescriptors.Count - 1].Version != expectedVersion && expectedVersion != -1)
            {
                throw new ConcurrencyException();
            }
            var i = expectedVersion;
            foreach (var @event in events)
            {
                i++;
                @event.Version = i;
                eventDescriptors.Add(new EventDescriptor(
                        aggregateId, Event.ConvertEventToJson(@event),
                        i, @event.GetType().AssemblyQualifiedName, aggregateRootType.AssemblyQualifiedName));
                

                _publisher.Publish(@event);
            }
        }

        public List<Event> GetEventsForAggregate(Guid aggregateId, Assembly assembly = null)
        {
            // todo - could optimise to not retrieve other columns.
            List<Event> events = new List<Event>();
            foreach (EventDescriptor descriptor in GetEventDescriptorsForAggregate(aggregateId, assembly))
            {
                events.Add(Event.ConvertJsonToEvent(descriptor.EventJson, Type.GetType(descriptor.EventType)));
            }
            return events;
        }

        private IEnumerable<EventDescriptor> GetEventDescriptorsForAggregate(Guid aggregateId, Assembly assembly)
        {
            return _events[aggregateId];
        }

        public List<AggregateRootModel> GetAggregateRoots(Type AggregrateRootType)
        {
            List<AggregateRootModel> aggregateRoots = new List<AggregateRootModel>();

            return (from agg in _aggregateRoots
                    where agg.Value.Type == AggregrateRootType.AssemblyQualifiedName
                    select agg).Select(p => p.Value).ToList();
        }

        public void Dispose()
        {
        }
    }

}